package tbs.framework.boardcast;

import cn.hutool.extra.spring.SpringUtil;
import tbs.framework.boardcast.notify.impl.SimpleNotifying;
import tbs.framework.boardcast.obsever.IObserver;
import tbs.framework.boardcast.subject.ISubject;
import tbs.framework.log.ILogger;
import tbs.framework.log.annotations.AutoLogger;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author tongj
 */
public class BoardcastHelper {
    private static final Map<Long, ISubject> subjects = new ConcurrentHashMap<>(32);

    @AutoLogger
    ILogger logger;

    @Resource
    private Map<String, IObserver> observers;

    @PostConstruct
    public void init() {
        subjects.putAll(SpringUtil.getBeansOfType(ISubject.class).entrySet().stream().map(e -> {
            ISubject m = e.getValue();
            String k = e.getKey();
            for (IObserver observer : observers.values()) {
                if (observer == null) {
                    continue;
                }
                if (observer.getSupportedSubjectIdSet().contains(m.getSubjectId())) {
                    m.addObserver(observer);
                }
            }
            if (m.getObserverCount() == 0) {
                logger.warn("{}-{}主题没有观察者", k, m.getSubjectId());
            }
            return m;
        }).collect(Collectors.toMap(ISubject::getSubjectId, m -> m)));
    }

    /**
     * 创建一个创建通知对象
     *
     * @param subject
     * @param data
     * @return
     */
    public static final <R> SimpleNotifying<R> makeCreateNotifying(ISubject subject, R data) {
        return makeCustomNotifying(SimpleNotifying.CREATE, subject, data);
    }

    /**
     * 创建一个更新通知对象
     *
     * @param subject
     * @param data
     * @return
     */
    public static final <R> SimpleNotifying<R> makeUpdateNotifying(ISubject subject, R data) {
        return makeCustomNotifying(SimpleNotifying.UPDATE, subject, data);
    }

    /**
     * 创建一个删除通知对象
     *
     * @param subject
     * @param data
     * @param <R>
     * @return
     */
    public static final <R> SimpleNotifying<R> makeDeleteNotifying(ISubject subject, R data) {
        return makeCustomNotifying(SimpleNotifying.DELETE, subject, data);
    }

    public static final <R> SimpleNotifying<R> makeCustomNotifying(Integer type, ISubject subject, R data) {
        return new SimpleNotifying(type, SimpleNotifying.IDLE, subject, data);
    }

    public final <R> void notifyOn(R data, Integer type, Long... subjectIds) {
        if (subjectIds == null || subjectIds.length == 0) {
            for (ISubject subject : subjects.values()) {
                subject.notifyObservers(makeCustomNotifying(type, subject, data));
            }
        }
        for (Long subjectId : subjectIds) {
            ISubject subject = subjects.get(subjectId);
            if (subject != null) {
                subject.notifyObservers(makeCustomNotifying(type, subject, data));
            }
        }
    }

    public final <R> void notifyCreateOn(R data, Long... subjectIds) {
        notifyOn(data, SimpleNotifying.CREATE, subjectIds);
    }

    public final <R> void notifyUpdateOn(R data, Long... subjectIds) {
        notifyOn(data, SimpleNotifying.UPDATE, subjectIds);
    }

    public final <R> void notifyDeleteOn(R data, Long... subjectIds) {
        notifyOn(data, SimpleNotifying.DELETE, subjectIds);
    }

    public void addObserver(Long subjectId, IObserver observer) {
        ISubject subject = subjects.get(subjectId);
        if (subject != null) {
            subject.addObserver(observer);
        }
    }

    public void removeObserver(Long subjectId, IObserver observer) {
        ISubject subject = subjects.get(subjectId);
        if (subject != null) {
            subject.removeObserver(observer);
        }
    }

    public void registerSubject(ISubject subject) {
        subjects.put(subject.getSubjectId(), subject);
    }

    public void unregisterSubject(ISubject subject) {
        subjects.remove(subject.getSubjectId());
    }
}
